﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace SoftPlc
{
	public class S7Consts
	{
		public const string Snap7LibName = "snap7";
		//------------------------------------------------------------------------------
		//                                  PARAMS LIST            
		//------------------------------------------------------------------------------
		public static readonly Int32 p_u16_LocalPort = 1;
		public static readonly Int32 p_u16_RemotePort = 2;
		public static readonly Int32 p_i32_PingTimeout = 3;
		public static readonly Int32 p_i32_SendTimeout = 4;
		public static readonly Int32 p_i32_RecvTimeout = 5;
		public static readonly Int32 p_i32_WorkInterval = 6;
		public static readonly Int32 p_u16_SrcRef = 7;
		public static readonly Int32 p_u16_DstRef = 8;
		public static readonly Int32 p_u16_SrcTSap = 9;
		public static readonly Int32 p_i32_PDURequest = 10;
		public static readonly Int32 p_i32_MaxClients = 11;
		public static readonly Int32 p_i32_BSendTimeout = 12;
		public static readonly Int32 p_i32_BRecvTimeout = 13;
		public static readonly Int32 p_u32_RecoveryTime = 14;
		public static readonly Int32 p_u32_KeepAliveTime = 15;

		[StructLayout(LayoutKind.Sequential, Pack = 1)]
		public struct S7Tag
		{
			public Int32 Area;
			public Int32 DBNumber;
			public Int32 Start;
			public Int32 Size;
			public Int32 WordLen;
		}
	}

	public static class S7
	{
		#region [Help Functions]

		private static Int64 bias = 621355968000000000; // "decimicros" between 0001-01-01 00:00:00 and 1970-01-01 00:00:00

		private static int BCDtoByte(byte B)
		{
			return ((B >> 4) * 10) + (B & 0x0F);
		}

		private static byte ByteToBCD(int Value)
		{
			return (byte)(((Value / 10) << 4) | (Value % 10));
		}

		private static byte[] CopyFrom(byte[] Buffer, int Pos, int Size)
		{
			byte[] Result = new byte[Size];
			Array.Copy(Buffer, Pos, Result, 0, Size);
			return Result;
		}

		#region Get/Set the bit at Pos.Bit
		public static bool GetBitAt(byte[] Buffer, int Pos, int Bit)
		{
			byte[] Mask = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
			if (Bit < 0) Bit = 0;
			if (Bit > 7) Bit = 7;
			return (Buffer[Pos] & Mask[Bit]) != 0;
		}
		public static void SetBitAt(ref byte[] Buffer, int Pos, int Bit, bool Value)
		{
			byte[] Mask = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
			if (Bit < 0) Bit = 0;
			if (Bit > 7) Bit = 7;

			if (Value)
				Buffer[Pos] = (byte)(Buffer[Pos] | Mask[Bit]);
			else
				Buffer[Pos] = (byte)(Buffer[Pos] & ~Mask[Bit]);
		}
		#endregion

		#region Get/Set 8 bit signed value (S7 SInt) -128..127
		public static int GetSIntAt(byte[] Buffer, int Pos)
		{
			int Value = Buffer[Pos];
			if (Value < 128)
				return Value;
			else
				return (int)(Value - 256);
		}
		public static void SetSIntAt(byte[] Buffer, int Pos, int Value)
		{
			if (Value < -128) Value = -128;
			if (Value > 127) Value = 127;
			Buffer[Pos] = (byte)Value;
		}
		#endregion

		#region Get/Set 16 bit signed value (S7 int) -32768..32767
		public static int GetIntAt(byte[] Buffer, int Pos)
		{
			return (int)((Buffer[Pos] << 8) | Buffer[Pos + 1]);
		}
		public static void SetIntAt(byte[] Buffer, int Pos, Int16 Value)
		{
			Buffer[Pos] = (byte)(Value >> 8);
			Buffer[Pos + 1] = (byte)(Value & 0x00FF);
		}
		#endregion

		#region Get/Set 32 bit signed value (S7 DInt) -2147483648..2147483647
		public static int GetDIntAt(byte[] Buffer, int Pos)
		{
			int Result;
			Result = Buffer[Pos]; Result <<= 8;
			Result += Buffer[Pos + 1]; Result <<= 8;
			Result += Buffer[Pos + 2]; Result <<= 8;
			Result += Buffer[Pos + 3];
			return Result;
		}
		public static void SetDIntAt(byte[] Buffer, int Pos, int Value)
		{
			Buffer[Pos + 3] = (byte)(Value & 0xFF);
			Buffer[Pos + 2] = (byte)((Value >> 8) & 0xFF);
			Buffer[Pos + 1] = (byte)((Value >> 16) & 0xFF);
			Buffer[Pos] = (byte)((Value >> 24) & 0xFF);
		}
		#endregion

		#region Get/Set 64 bit signed value (S7 LInt) -9223372036854775808..9223372036854775807
		public static Int64 GetLIntAt(byte[] Buffer, int Pos)
		{
			Int64 Result;
			Result = Buffer[Pos]; Result <<= 8;
			Result += Buffer[Pos + 1]; Result <<= 8;
			Result += Buffer[Pos + 2]; Result <<= 8;
			Result += Buffer[Pos + 3]; Result <<= 8;
			Result += Buffer[Pos + 4]; Result <<= 8;
			Result += Buffer[Pos + 5]; Result <<= 8;
			Result += Buffer[Pos + 6]; Result <<= 8;
			Result += Buffer[Pos + 7];
			return Result;
		}
		public static void SetLIntAt(byte[] Buffer, int Pos, Int64 Value)
		{
			Buffer[Pos + 7] = (byte)(Value & 0xFF);
			Buffer[Pos + 6] = (byte)((Value >> 8) & 0xFF);
			Buffer[Pos + 5] = (byte)((Value >> 16) & 0xFF);
			Buffer[Pos + 4] = (byte)((Value >> 24) & 0xFF);
			Buffer[Pos + 3] = (byte)((Value >> 32) & 0xFF);
			Buffer[Pos + 2] = (byte)((Value >> 40) & 0xFF);
			Buffer[Pos + 1] = (byte)((Value >> 48) & 0xFF);
			Buffer[Pos] = (byte)((Value >> 56) & 0xFF);
		}
		#endregion

		#region Get/Set 8 bit unsigned value (S7 USInt) 0..255
		public static byte GetUSIntAt(byte[] Buffer, int Pos)
		{
			return Buffer[Pos];
		}
		public static void SetUSIntAt(byte[] Buffer, int Pos, byte Value)
		{
			Buffer[Pos] = Value;
		}
		#endregion

		#region Get/Set 16 bit unsigned value (S7 UInt) 0..65535
		public static UInt16 GetUIntAt(byte[] Buffer, int Pos)
		{
			return (UInt16)((Buffer[Pos] << 8) | Buffer[Pos + 1]);
		}
		public static void SetUIntAt(byte[] Buffer, int Pos, UInt16 Value)
		{
			Buffer[Pos] = (byte)(Value >> 8);
			Buffer[Pos + 1] = (byte)(Value & 0x00FF);
		}
		#endregion

		#region Get/Set 32 bit unsigned value (S7 UDInt) 0..4294967296
		public static UInt32 GetUDIntAt(byte[] Buffer, int Pos)
		{
			UInt32 Result;
			Result = Buffer[Pos]; Result <<= 8;
			Result |= Buffer[Pos + 1]; Result <<= 8;
			Result |= Buffer[Pos + 2]; Result <<= 8;
			Result |= Buffer[Pos + 3];
			return Result;
		}
		public static void SetUDIntAt(byte[] Buffer, int Pos, UInt32 Value)
		{
			Buffer[Pos + 3] = (byte)(Value & 0xFF);
			Buffer[Pos + 2] = (byte)((Value >> 8) & 0xFF);
			Buffer[Pos + 1] = (byte)((Value >> 16) & 0xFF);
			Buffer[Pos] = (byte)((Value >> 24) & 0xFF);
		}
		#endregion

		#region Get/Set 64 bit unsigned value (S7 ULint) 0..18446744073709551616
		public static UInt64 GetULIntAt(byte[] Buffer, int Pos)
		{
			UInt64 Result;
			Result = Buffer[Pos]; Result <<= 8;
			Result |= Buffer[Pos + 1]; Result <<= 8;
			Result |= Buffer[Pos + 2]; Result <<= 8;
			Result |= Buffer[Pos + 3]; Result <<= 8;
			Result |= Buffer[Pos + 4]; Result <<= 8;
			Result |= Buffer[Pos + 5]; Result <<= 8;
			Result |= Buffer[Pos + 6]; Result <<= 8;
			Result |= Buffer[Pos + 7];
			return Result;
		}
		public static void SetULintAt(byte[] Buffer, int Pos, UInt64 Value)
		{
			Buffer[Pos + 7] = (byte)(Value & 0xFF);
			Buffer[Pos + 6] = (byte)((Value >> 8) & 0xFF);
			Buffer[Pos + 5] = (byte)((Value >> 16) & 0xFF);
			Buffer[Pos + 4] = (byte)((Value >> 24) & 0xFF);
			Buffer[Pos + 3] = (byte)((Value >> 32) & 0xFF);
			Buffer[Pos + 2] = (byte)((Value >> 40) & 0xFF);
			Buffer[Pos + 1] = (byte)((Value >> 48) & 0xFF);
			Buffer[Pos] = (byte)((Value >> 56) & 0xFF);
		}
		#endregion

		#region Get/Set 8 bit word (S7 Byte) 16#00..16#FF
		public static byte GetByteAt(byte[] Buffer, int Pos)
		{
			return Buffer[Pos];
		}
		public static void SetByteAt(byte[] Buffer, int Pos, byte Value)
		{
			Buffer[Pos] = Value;
		}
		#endregion

		#region Get/Set 16 bit word (S7 Word) 16#0000..16#FFFF
		public static UInt16 GetWordAt(byte[] Buffer, int Pos)
		{
			return GetUIntAt(Buffer, Pos);
		}
		public static void SetWordAt(byte[] Buffer, int Pos, UInt16 Value)
		{
			SetUIntAt(Buffer, Pos, Value);
		}
		#endregion

		#region Get/Set 32 bit word (S7 DWord) 16#00000000..16#FFFFFFFF
		public static UInt32 GetDWordAt(byte[] Buffer, int Pos)
		{
			return GetUDIntAt(Buffer, Pos);
		}
		public static void SetDWordAt(byte[] Buffer, int Pos, UInt32 Value)
		{
			SetUDIntAt(Buffer, Pos, Value);
		}
		#endregion

		#region Get/Set 64 bit word (S7 LWord) 16#0000000000000000..16#FFFFFFFFFFFFFFFF
		public static UInt64 GetLWordAt(byte[] Buffer, int Pos)
		{
			return GetULIntAt(Buffer, Pos);
		}
		public static void SetLWordAt(byte[] Buffer, int Pos, UInt64 Value)
		{
			SetULintAt(Buffer, Pos, Value);
		}
		#endregion

		#region Get/Set 32 bit floating point number (S7 Real) (Range of Single)
		public static Single GetRealAt(byte[] Buffer, int Pos)
		{
			UInt32 Value = GetUDIntAt(Buffer, Pos);
			byte[] bytes = BitConverter.GetBytes(Value);
			return BitConverter.ToSingle(bytes, 0);
		}
		public static void SetRealAt(byte[] Buffer, int Pos, Single Value)
		{
			byte[] FloatArray = BitConverter.GetBytes(Value);
			Buffer[Pos] = FloatArray[3];
			Buffer[Pos + 1] = FloatArray[2];
			Buffer[Pos + 2] = FloatArray[1];
			Buffer[Pos + 3] = FloatArray[0];
		}
		#endregion

		#region Get/Set 64 bit floating point number (S7 LReal) (Range of Double)
		public static Double GetLRealAt(byte[] Buffer, int Pos)
		{
			UInt64 Value = GetULIntAt(Buffer, Pos);
			byte[] bytes = BitConverter.GetBytes(Value);
			return BitConverter.ToDouble(bytes, 0);
		}
		public static void SetLRealAt(byte[] Buffer, int Pos, Double Value)
		{
			byte[] FloatArray = BitConverter.GetBytes(Value);
			Buffer[Pos] = FloatArray[7];
			Buffer[Pos + 1] = FloatArray[6];
			Buffer[Pos + 2] = FloatArray[5];
			Buffer[Pos + 3] = FloatArray[4];
			Buffer[Pos + 4] = FloatArray[3];
			Buffer[Pos + 5] = FloatArray[2];
			Buffer[Pos + 6] = FloatArray[1];
			Buffer[Pos + 7] = FloatArray[0];
		}
		#endregion

		#region Get/Set DateTime (S7 DATE_AND_TIME)
		public static DateTime GetDateTimeAt(byte[] Buffer, int Pos)
		{
			int Year, Month, Day, Hour, Min, Sec, MSec;

			Year = BCDtoByte(Buffer[Pos]);
			if (Year < 90)
				Year += 2000;
			else
				Year += 1900;

			Month = BCDtoByte(Buffer[Pos + 1]);
			Day = BCDtoByte(Buffer[Pos + 2]);
			Hour = BCDtoByte(Buffer[Pos + 3]);
			Min = BCDtoByte(Buffer[Pos + 4]);
			Sec = BCDtoByte(Buffer[Pos + 5]);
			MSec = (BCDtoByte(Buffer[Pos + 6]) * 10) + (BCDtoByte(Buffer[Pos + 7]) / 10);
			try
			{
				return new DateTime(Year, Month, Day, Hour, Min, Sec, MSec);
			}
			catch (System.ArgumentOutOfRangeException)
			{
				return new DateTime(0);
			}
		}
		public static void SetDateTimeAt(byte[] Buffer, int Pos, DateTime Value)
		{
			int Year = Value.Year;
			int Month = Value.Month;
			int Day = Value.Day;
			int Hour = Value.Hour;
			int Min = Value.Minute;
			int Sec = Value.Second;
			int Dow = (int)Value.DayOfWeek + 1;
			// MSecH = First two digits of miliseconds 
			int MsecH = Value.Millisecond / 10;
			// MSecL = Last digit of miliseconds
			int MsecL = Value.Millisecond % 10;
			if (Year > 1999)
				Year -= 2000;

			Buffer[Pos] = ByteToBCD(Year);
			Buffer[Pos + 1] = ByteToBCD(Month);
			Buffer[Pos + 2] = ByteToBCD(Day);
			Buffer[Pos + 3] = ByteToBCD(Hour);
			Buffer[Pos + 4] = ByteToBCD(Min);
			Buffer[Pos + 5] = ByteToBCD(Sec);
			Buffer[Pos + 6] = ByteToBCD(MsecH);
			Buffer[Pos + 7] = ByteToBCD(MsecL * 10 + Dow);
		}
		#endregion

		#region Get/Set DATE (S7 DATE) 
		public static DateTime GetDateAt(byte[] Buffer, int Pos)
		{
			try
			{
				return new DateTime(1990, 1, 1).AddDays(GetIntAt(Buffer, Pos));
			}
			catch (System.ArgumentOutOfRangeException)
			{
				return new DateTime(0);
			}
		}
		public static void SetDateAt(byte[] Buffer, int Pos, DateTime Value)
		{
			SetIntAt(Buffer, Pos, (Int16)(Value - new DateTime(1990, 1, 1)).Days);
		}

		#endregion

		#region Get/Set TOD (S7 TIME_OF_DAY)
		public static DateTime GetTODAt(byte[] Buffer, int Pos)
		{
			try
			{
				return new DateTime(0).AddMilliseconds(S7.GetDIntAt(Buffer, Pos));
			}
			catch (System.ArgumentOutOfRangeException)
			{
				return new DateTime(0);
			}
		}
		public static void SetTODAt(byte[] Buffer, int Pos, DateTime Value)
		{
			TimeSpan Time = Value.TimeOfDay;
			SetDIntAt(Buffer, Pos, (Int32)Math.Round(Time.TotalMilliseconds));
		}
		#endregion

		#region Get/Set LTOD (S7 1500 LONG TIME_OF_DAY)
		public static DateTime GetLTODAt(byte[] Buffer, int Pos)
		{
			// .NET Tick = 100 ns, S71500 Tick = 1 ns
			try
			{
				return new DateTime(Math.Abs(GetLIntAt(Buffer, Pos) / 100));
			}
			catch (System.ArgumentOutOfRangeException)
			{
				return new DateTime(0);
			}
		}
		public static void SetLTODAt(byte[] Buffer, int Pos, DateTime Value)
		{
			TimeSpan Time = Value.TimeOfDay;
			SetLIntAt(Buffer, Pos, (Int64)Time.Ticks * 100);
		}

		#endregion

		#region GET/SET LDT (S7 1500 Long Date and Time)
		public static DateTime GetLDTAt(byte[] Buffer, int Pos)
		{
			try
			{
				return new DateTime((GetLIntAt(Buffer, Pos) / 100) + bias);
			}
			catch (System.ArgumentOutOfRangeException)
			{
				return new DateTime(0);
			}
		}
		public static void SetLDTAt(byte[] Buffer, int Pos, DateTime Value)
		{
			SetLIntAt(Buffer, Pos, (Value.Ticks - bias) * 100);
		}
		#endregion

		#region Get/Set DTL (S71200/1500 Date and Time)
		// Thanks to Johan Cardoen for GetDTLAt
		public static DateTime GetDTLAt(byte[] Buffer, int Pos)
		{
			int Year, Month, Day, Hour, Min, Sec, MSec;

			Year = Buffer[Pos] * 256 + Buffer[Pos + 1];
			Month = Buffer[Pos + 2];
			Day = Buffer[Pos + 3];
			Hour = Buffer[Pos + 5];
			Min = Buffer[Pos + 6];
			Sec = Buffer[Pos + 7];
			MSec = (int)GetUDIntAt(Buffer, Pos + 8) / 1000000;

			try
			{
				return new DateTime(Year, Month, Day, Hour, Min, Sec, MSec);
			}
			catch (System.ArgumentOutOfRangeException)
			{
				return new DateTime(0);
			}
		}
		public static void SetDTLAt(byte[] Buffer, int Pos, DateTime Value)
		{
			short Year = (short)Value.Year;
			byte Month = (byte)Value.Month;
			byte Day = (byte)Value.Day;
			byte Hour = (byte)Value.Hour;
			byte Min = (byte)Value.Minute;
			byte Sec = (byte)Value.Second;
			byte Dow = (byte)(Value.DayOfWeek + 1);

			Int32 NanoSecs = Value.Millisecond * 1000000;

			var bytes_short = BitConverter.GetBytes(Year);

			Buffer[Pos] = bytes_short[1];
			Buffer[Pos + 1] = bytes_short[0];
			Buffer[Pos + 2] = Month;
			Buffer[Pos + 3] = Day;
			Buffer[Pos + 4] = Dow;
			Buffer[Pos + 5] = Hour;
			Buffer[Pos + 6] = Min;
			Buffer[Pos + 7] = Sec;
			SetDIntAt(Buffer, Pos + 8, NanoSecs);
		}
		#endregion

		#region Get/Set String (S7 String)
		// Thanks to Pablo Agirre 
		public static string GetStringAt(byte[] Buffer, int Pos)
		{
			int size = (int)Buffer[Pos + 1];
			return Encoding.ASCII.GetString(Buffer, Pos + 2, size);
		}
		public static void SetStringAt(byte[] Buffer, int Pos, int MaxLen, string Value)
		{
			int size = Value.Length;
			Buffer[Pos] = (byte)MaxLen;
			Buffer[Pos + 1] = (byte)size;
			Encoding.ASCII.GetBytes(Value, 0, size, Buffer, Pos + 2);
		}
		#endregion

		#region Get/Set Array of char (S7 ARRAY OF CHARS)
		public static string GetCharsAt(byte[] Buffer, int Pos, int Size)
		{
			return Encoding.ASCII.GetString(Buffer, Pos, Size);
		}
		public static void SetCharsAt(byte[] Buffer, int Pos, string Value)
		{
			int MaxLen = Buffer.Length - Pos;
			// Truncs the string if there's no room enough        
			if (MaxLen > Value.Length) MaxLen = Value.Length;
			Encoding.ASCII.GetBytes(Value, 0, MaxLen, Buffer, Pos);
		}
		#endregion

		#endregion [Help Functions]
	}

	public class S7MultiVar
	{
		#region [MultiRead/Write Helper]
		private S7Client FClient;
		private GCHandle[] Handles = new GCHandle[S7Client.MaxVars];
		private int Count = 0;
		private S7Client.S7DataItem[] Items = new S7Client.S7DataItem[S7Client.MaxVars];

		public int[] Results = new int[S7Client.MaxVars];

		public S7MultiVar(S7Client Client)
		{
			FClient = Client;
			for (int c = 0; c < S7Client.MaxVars; c++)
				Results[c] = (int)S7Client.errCliItemNotAvailable;
		}
		~S7MultiVar()
		{
			Clear();
		}

		public bool Add<T>(Int32 Area, Int32 WordLen, Int32 DBNumber, Int32 Start, Int32 Amount, ref T[] Buffer)
		{
			return Add(Area, WordLen, DBNumber, Start, Amount, ref Buffer, 0);
		}

		public bool Add<T>(Int32 Area, Int32 WordLen, Int32 DBNumber, Int32 Start, Int32 Amount, ref T[] Buffer, int Offset)
		{
			if (Count < S7Client.MaxVars)
			{
				Items[Count].Area = Area;
				Items[Count].WordLen = WordLen;
				Items[Count].Result = 0;
				Items[Count].DBNumber = DBNumber;
				Items[Count].Start = Start;
				Items[Count].Amount = Amount;
				GCHandle handle = GCHandle.Alloc(Buffer, GCHandleType.Pinned);
				if (IntPtr.Size == 4)
					Items[Count].pData = (IntPtr)(handle.AddrOfPinnedObject().ToInt32() + Offset * Marshal.SizeOf(typeof(T)));
				else
					Items[Count].pData = (IntPtr)(handle.AddrOfPinnedObject().ToInt64() + Offset * Marshal.SizeOf(typeof(T)));

				Handles[Count] = handle;
				Count++;
				return true;
			}
			else
				return false;
		}

		public int Read()
		{
			int FunctionResult;
			if (Count > 0)
			{
				FunctionResult = FClient.ReadMultiVars(Items, Count);
				if (FunctionResult == 0)
					for (int c = 0; c < S7Client.MaxVars; c++)
						Results[c] = Items[c].Result;
				return FunctionResult;
			}
			else
				return (int)S7Client.errCliFunctionRefused;
		}
		public int Write()
		{
			int FunctionResult;
			if (Count > 0)
			{
				FunctionResult = FClient.WriteMultiVars(Items, Count);
				if (FunctionResult == 0)
					for (int c = 0; c < S7Client.MaxVars; c++)
						Results[c] = Items[c].Result;
				return FunctionResult;
			}
			else
				return (int)S7Client.errCliFunctionRefused;
		}
		public void Clear()
		{
			for (int c = 0; c < Count; c++)
			{
				if (Handles[c] != null)
					Handles[c].Free();
			}
			for (int c = 0; c < S7Client.MaxVars; c++)
				Results[c] = (int)S7Client.errCliItemNotAvailable;
			Count = 0;
		}
		#endregion
	}

	public class S7Client
	{
		#region [Constants, private vars and TypeDefs]
		private const int MsgTextLen = 1024;
		// Error codes
		public static readonly uint errNegotiatingPDU = 0x00100000;
		public static readonly uint errCliInvalidParams = 0x00200000;
		public static readonly uint errCliJobPending = 0x00300000;
		public static readonly uint errCliTooManyItems = 0x00400000;
		public static readonly uint errCliInvalidWordLen = 0x00500000;
		public static readonly uint errCliPartialDataWritten = 0x00600000;
		public static readonly uint errCliSizeOverPDU = 0x00700000;
		public static readonly uint errCliInvalidPlcAnswer = 0x00800000;
		public static readonly uint errCliAddressOutOfRange = 0x00900000;
		public static readonly uint errCliInvalidTransportSize = 0x00A00000;
		public static readonly uint errCliWriteDataSizeMismatch = 0x00B00000;
		public static readonly uint errCliItemNotAvailable = 0x00C00000;
		public static readonly uint errCliInvalidValue = 0x00D00000;
		public static readonly uint errCliCannotStartPLC = 0x00E00000;
		public static readonly uint errCliAlreadyRun = 0x00F00000;
		public static readonly uint errCliCannotStopPLC = 0x01000000;
		public static readonly uint errCliCannotCopyRamToRom = 0x01100000;
		public static readonly uint errCliCannotCompress = 0x01200000;
		public static readonly uint errCliAlreadyStop = 0x01300000;
		public static readonly uint errCliFunNotAvailable = 0x01400000;
		public static readonly uint errCliUploadSequenceFailed = 0x01500000;
		public static readonly uint errCliInvalidDataSizeRecvd = 0x01600000;
		public static readonly uint errCliInvalidBlockType = 0x01700000;
		public static readonly uint errCliInvalidBlockNumber = 0x01800000;
		public static readonly uint errCliInvalidBlockSize = 0x01900000;
		public static readonly uint errCliDownloadSequenceFailed = 0x01A00000;
		public static readonly uint errCliInsertRefused = 0x01B00000;
		public static readonly uint errCliDeleteRefused = 0x01C00000;
		public static readonly uint errCliNeedPassword = 0x01D00000;
		public static readonly uint errCliInvalidPassword = 0x01E00000;
		public static readonly uint errCliNoPasswordToSetOrClear = 0x01F00000;
		public static readonly uint errCliJobTimeout = 0x02000000;
		public static readonly uint errCliPartialDataRead = 0x02100000;
		public static readonly uint errCliBufferTooSmall = 0x02200000;
		public static readonly uint errCliFunctionRefused = 0x02300000;
		public static readonly uint errCliDestroying = 0x02400000;
		public static readonly uint errCliInvalidParamNumber = 0x02500000;
		public static readonly uint errCliCannotChangeParam = 0x02600000;

		// Area ID
		public static readonly byte S7AreaPE = 0x81;
		public static readonly byte S7AreaPA = 0x82;
		public static readonly byte S7AreaMK = 0x83;
		public static readonly byte S7AreaDB = 0x84;
		public static readonly byte S7AreaCT = 0x1C;
		public static readonly byte S7AreaTM = 0x1D;

		// Word Length
		public static readonly int S7WLBit = 0x01;
		public static readonly int S7WLByte = 0x02;
		public static readonly int S7WLWord = 0x04;
		public static readonly int S7WLDWord = 0x06;
		public static readonly int S7WLReal = 0x08;
		public static readonly int S7WLCounter = 0x1C;
		public static readonly int S7WLTimer = 0x1D;

		// Block type
		public static readonly byte Block_OB = 0x38;
		public static readonly byte Block_DB = 0x41;
		public static readonly byte Block_SDB = 0x42;
		public static readonly byte Block_FC = 0x43;
		public static readonly byte Block_SFC = 0x44;
		public static readonly byte Block_FB = 0x45;
		public static readonly byte Block_SFB = 0x46;

		// Sub Block Type 
		public static readonly byte SubBlk_OB = 0x08;
		public static readonly byte SubBlk_DB = 0x0A;
		public static readonly byte SubBlk_SDB = 0x0B;
		public static readonly byte SubBlk_FC = 0x0C;
		public static readonly byte SubBlk_SFC = 0x0D;
		public static readonly byte SubBlk_FB = 0x0E;
		public static readonly byte SubBlk_SFB = 0x0F;

		// Block languages
		public static readonly byte BlockLangAWL = 0x01;
		public static readonly byte BlockLangKOP = 0x02;
		public static readonly byte BlockLangFUP = 0x03;
		public static readonly byte BlockLangSCL = 0x04;
		public static readonly byte BlockLangDB = 0x05;
		public static readonly byte BlockLangGRAPH = 0x06;

		// Max number of vars (multiread/write)
		public static readonly int MaxVars = 20;

		// Client Connection Type
		public static readonly UInt16 CONNTYPE_PG = 0x01;  // Connect to the PLC as a PG
		public static readonly UInt16 CONNTYPE_OP = 0x02;  // Connect to the PLC as an OP
		public static readonly UInt16 CONNTYPE_BASIC = 0x03;  // Basic connection 

		// Job
		private const int JobComplete = 0;
		private const int JobPending = 1;

		private IntPtr Client;

		// New Data Item, Thanks to LanceL
		[StructLayout(LayoutKind.Sequential, Pack = 1)]
		public struct S7DataItem
		{
			public Int32 Area;
			public Int32 WordLen;
			public Int32 Result;
			public Int32 DBNumber;
			public Int32 Start;
			public Int32 Amount;
			public IntPtr pData;
		}

		// Block List
		[StructLayout(LayoutKind.Sequential, Pack = 1)] // <- "maybe" we don't need
		public struct S7BlocksList
		{
			public Int32 OBCount;
			public Int32 FBCount;
			public Int32 FCCount;
			public Int32 SFBCount;
			public Int32 SFCCount;
			public Int32 DBCount;
			public Int32 SDBCount;
		};

		// Packed Block Info
		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
		protected struct US7BlockInfo
		{
			public Int32 BlkType;
			public Int32 BlkNumber;
			public Int32 BlkLang;
			public Int32 BlkFlags;
			public Int32 MC7Size;  // The real size in bytes
			public Int32 LoadSize;
			public Int32 LocalData;
			public Int32 SBBLength;
			public Int32 CheckSum;
			public Int32 Version;
			// Chars info
			[MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)]
			public char[] CodeDate;
			[MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)]
			public char[] IntfDate;
			[MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
			public char[] Author;
			[MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
			public char[] Family;
			[MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
			public char[] Header;
		};
		private US7BlockInfo UBlockInfo;

		// Managed Block Info
		public struct S7BlockInfo
		{
			public int BlkType;
			public int BlkNumber;
			public int BlkLang;
			public int BlkFlags;
			public int MC7Size;  // The real size in bytes
			public int LoadSize;
			public int LocalData;
			public int SBBLength;
			public int CheckSum;
			public int Version;
			// Chars info
			public string CodeDate;
			public string IntfDate;
			public string Author;
			public string Family;
			public string Header;
		};

		public ushort[] TS7BlocksOfType;

		// Packed Order Code + Version
		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
		protected struct US7OrderCode
		{
			[MarshalAs(UnmanagedType.ByValArray, SizeConst = 21)]
			public char[] Code;
			public byte V1;
			public byte V2;
			public byte V3;
		};
		private US7OrderCode UOrderCode;

		// Managed Order Code + Version
		public struct S7OrderCode
		{
			public string Code;
			public byte V1;
			public byte V2;
			public byte V3;
		};

		// Packed CPU Info
		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
		protected struct US7CpuInfo
		{
			[MarshalAs(UnmanagedType.ByValArray, SizeConst = 33)]
			public char[] ModuleTypeName;
			[MarshalAs(UnmanagedType.ByValArray, SizeConst = 25)]
			public char[] SerialNumber;
			[MarshalAs(UnmanagedType.ByValArray, SizeConst = 25)]
			public char[] ASName;
			[MarshalAs(UnmanagedType.ByValArray, SizeConst = 27)]
			public char[] Copyright;
			[MarshalAs(UnmanagedType.ByValArray, SizeConst = 25)]
			public char[] ModuleName;
		};
		private US7CpuInfo UCpuInfo;

		// Managed CPU Info
		public struct S7CpuInfo
		{
			public string ModuleTypeName;
			public string SerialNumber;
			public string ASName;
			public string Copyright;
			public string ModuleName;
		}

		[StructLayout(LayoutKind.Sequential, Pack = 1)]
		public struct S7CpInfo
		{
			public Int32 MaxPduLengt;
			public Int32 MaxConnections;
			public Int32 MaxMpiRate;
			public Int32 MaxBusRate;
		};

		// See §33.1 of "System Software for S7-300/400 System and Standard Functions"
		// and see SFC51 description too
		[StructLayout(LayoutKind.Sequential, Pack = 1)]
		public struct SZL_HEADER
		{
			public UInt16 LENTHDR;
			public UInt16 N_DR;
		};

		[StructLayout(LayoutKind.Sequential, Pack = 1)]
		public struct S7SZL
		{
			public SZL_HEADER Header;
			[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x4000 - 4)]
			public byte[] Data;
		};

		// SZL List of available SZL IDs : same as SZL but List items are big-endian adjusted
		[StructLayout(LayoutKind.Sequential, Pack = 1)]
		public struct S7SZLList
		{
			public SZL_HEADER Header;
			[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x2000 - 2)]
			public UInt16[] Data;
		};

		// S7 Protection
		// See §33.19 of "System Software for S7-300/400 System and Standard Functions"
		[StructLayout(LayoutKind.Sequential, Pack = 1)]
		public struct S7Protection  // Packed S7Protection
		{
			public UInt16 sch_schal;
			public UInt16 sch_par;
			public UInt16 sch_rel;
			public UInt16 bart_sch;
			public UInt16 anl_sch;
		};

		// C++ time struct, functions to convert it from/to DateTime are provided ;-)
		[StructLayout(LayoutKind.Sequential, Pack = 1)]
		protected struct cpp_tm
		{
			public Int32 tm_sec;
			public Int32 tm_min;
			public Int32 tm_hour;
			public Int32 tm_mday;
			public Int32 tm_mon;
			public Int32 tm_year;
			public Int32 tm_wday;
			public Int32 tm_yday;
			public Int32 tm_isdst;
		}
		private cpp_tm tm;

		#endregion

		#region [Class Control]

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern IntPtr Cli_Create();
		public S7Client()
		{
			Client = Cli_Create();
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_Destroy(ref IntPtr Client);
		~S7Client()
		{
			Cli_Destroy(ref Client);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_Connect(IntPtr Client);
		public int Connect()
		{
			return Cli_Connect(Client);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_ConnectTo(IntPtr Client,
			[MarshalAs(UnmanagedType.LPStr)] string Address,
			int Rack,
			int Slot
			);

		public int ConnectTo(string Address, int Rack, int Slot)
		{
			return Cli_ConnectTo(Client, Address, Rack, Slot);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_SetConnectionParams(IntPtr Client,
			[MarshalAs(UnmanagedType.LPStr)] string Address,
			UInt16 LocalTSAP,
			UInt16 RemoteTSAP
			);

		public int SetConnectionParams(string Address, UInt16 LocalTSAP, UInt16 RemoteTSAP)
		{
			return Cli_SetConnectionParams(Client, Address, LocalTSAP, RemoteTSAP);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_SetConnectionType(IntPtr Client, UInt16 ConnectionType);
		public int SetConnectionType(UInt16 ConnectionType)
		{
			return Cli_SetConnectionType(Client, ConnectionType);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_Disconnect(IntPtr Client);
		public int Disconnect()
		{
			return Cli_Disconnect(Client);
		}

		// Get/SetParam needs a void* parameter, internally it decides the kind of pointer
		// in accord to ParamNumber.
		// To avoid the use of unsafe code we split the DLL functions and use overloaded methods.

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Cli_GetParam")]
		protected static extern int Cli_GetParam_i16(IntPtr Client, Int32 ParamNumber, ref Int16 IntValue);
		public int GetParam(Int32 ParamNumber, ref Int16 IntValue)
		{
			return Cli_GetParam_i16(Client, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Cli_GetParam")]
		protected static extern int Cli_GetParam_u16(IntPtr Client, Int32 ParamNumber, ref UInt16 IntValue);
		public int GetParam(Int32 ParamNumber, ref UInt16 IntValue)
		{
			return Cli_GetParam_u16(Client, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Cli_GetParam")]
		protected static extern int Cli_GetParam_i32(IntPtr Client, Int32 ParamNumber, ref Int32 IntValue);
		public int GetParam(Int32 ParamNumber, ref Int32 IntValue)
		{
			return Cli_GetParam_i32(Client, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Cli_GetParam")]
		protected static extern int Cli_GetParam_u32(IntPtr Client, Int32 ParamNumber, ref UInt32 IntValue);
		public int GetParam(Int32 ParamNumber, ref UInt32 IntValue)
		{
			return Cli_GetParam_u32(Client, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Cli_GetParam")]
		protected static extern int Cli_GetParam_i64(IntPtr Client, Int32 ParamNumber, ref Int64 IntValue);
		public int GetParam(Int32 ParamNumber, ref Int64 IntValue)
		{
			return Cli_GetParam_i64(Client, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Cli_GetParam")]
		protected static extern int Cli_GetParam_u64(IntPtr Client, Int32 ParamNumber, ref UInt64 IntValue);
		public int GetParam(Int32 ParamNumber, ref UInt64 IntValue)
		{
			return Cli_GetParam_u64(Client, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Cli_SetParam")]
		protected static extern int Cli_SetParam_i16(IntPtr Client, Int32 ParamNumber, ref Int16 IntValue);
		public int SetParam(Int32 ParamNumber, ref Int16 IntValue)
		{
			return Cli_SetParam_i16(Client, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Cli_SetParam")]
		protected static extern int Cli_SetParam_u16(IntPtr Client, Int32 ParamNumber, ref UInt16 IntValue);
		public int SetParam(Int32 ParamNumber, ref UInt16 IntValue)
		{
			return Cli_SetParam_u16(Client, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Cli_SetParam")]
		protected static extern int Cli_SetParam_i32(IntPtr Client, Int32 ParamNumber, ref Int32 IntValue);
		public int SetParam(Int32 ParamNumber, ref Int32 IntValue)
		{
			return Cli_SetParam_i32(Client, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Cli_SetParam")]
		protected static extern int Cli_SetParam_u32(IntPtr Client, Int32 ParamNumber, ref UInt32 IntValue);
		public int SetParam(Int32 ParamNumber, ref UInt32 IntValue)
		{
			return Cli_SetParam_u32(Client, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Cli_SetParam")]
		protected static extern int Cli_SetParam_i64(IntPtr Client, Int32 ParamNumber, ref Int64 IntValue);
		public int SetParam(Int32 ParamNumber, ref Int64 IntValue)
		{
			return Cli_SetParam_i64(Client, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Cli_SetParam")]
		protected static extern int Cli_SetParam_u64(IntPtr Client, Int32 ParamNumber, ref UInt64 IntValue);
		public int SetParam(Int32 ParamNumber, ref UInt64 IntValue)
		{
			return Cli_SetParam_u64(Client, ParamNumber, ref IntValue);
		}

		public delegate void S7CliCompletion(IntPtr usrPtr, int opCode, int opResult);

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_SetAsCallback(IntPtr Client, S7CliCompletion Completion, IntPtr usrPtr);
		public int SetAsCallBack(S7CliCompletion Completion, IntPtr usrPtr)
		{
			return Cli_SetAsCallback(Client, Completion, usrPtr);
		}

		#endregion

		#region [Data I/O main functions]

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_ReadArea(IntPtr Client, int Area, int DBNumber, int Start, int Amount, int WordLen, byte[] Buffer);
		public int ReadArea(int Area, int DBNumber, int Start, int Amount, int WordLen, byte[] Buffer)
		{
			return Cli_ReadArea(Client, Area, DBNumber, Start, Amount, WordLen, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Cli_ReadArea")]
		protected static extern int Cli_ReadArea_ptr(IntPtr Client, int Area, int DBNumber, int Start, int Amount, int WordLen, IntPtr Pointer);
		public int ReadArea(int Area, int DBNumber, int Start, int Amount, int WordLen, IntPtr Pointer)
		{
			return Cli_ReadArea_ptr(Client, Area, DBNumber, Start, Amount, WordLen, Pointer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_WriteArea(IntPtr Client, int Area, int DBNumber, int Start, int Amount, int WordLen, byte[] Buffer);
		public int WriteArea(int Area, int DBNumber, int Start, int Amount, int WordLen, byte[] Buffer)
		{
			return Cli_WriteArea(Client, Area, DBNumber, Start, Amount, WordLen, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_ReadMultiVars(IntPtr Client, ref S7DataItem Item, int ItemsCount);
		public int ReadMultiVars(S7DataItem[] Items, int ItemsCount)
		{
			return Cli_ReadMultiVars(Client, ref Items[0], ItemsCount);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_WriteMultiVars(IntPtr Client, ref S7DataItem Item, int ItemsCount);
		public int WriteMultiVars(S7DataItem[] Items, int ItemsCount)
		{
			return Cli_WriteMultiVars(Client, ref Items[0], ItemsCount);
		}

		#endregion

		#region [Data I/O lean functions]

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_DBRead(IntPtr Client, int DBNumber, int Start, int Size, byte[] Buffer);
		public int DBRead(int DBNumber, int Start, int Size, byte[] Buffer)
		{
			return Cli_DBRead(Client, DBNumber, Start, Size, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_DBWrite(IntPtr Client, int DBNumber, int Start, int Size, byte[] Buffer);
		public int DBWrite(int DBNumber, int Start, int Size, byte[] Buffer)
		{
			return Cli_DBWrite(Client, DBNumber, Start, Size, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_MBRead(IntPtr Client, int Start, int Size, byte[] Buffer);
		public int MBRead(int Start, int Size, byte[] Buffer)
		{
			return Cli_MBRead(Client, Start, Size, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_MBWrite(IntPtr Client, int Start, int Size, byte[] Buffer);
		public int MBWrite(int Start, int Size, byte[] Buffer)
		{
			return Cli_MBWrite(Client, Start, Size, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_EBRead(IntPtr Client, int Start, int Size, byte[] Buffer);
		public int EBRead(int Start, int Size, byte[] Buffer)
		{
			return Cli_EBRead(Client, Start, Size, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_EBWrite(IntPtr Client, int Start, int Size, byte[] Buffer);
		public int EBWrite(int Start, int Size, byte[] Buffer)
		{
			return Cli_EBWrite(Client, Start, Size, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_ABRead(IntPtr Client, int Start, int Size, byte[] Buffer);
		public int ABRead(int Start, int Size, byte[] Buffer)
		{
			return Cli_ABRead(Client, Start, Size, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_ABWrite(IntPtr Client, int Start, int Size, byte[] Buffer);
		public int ABWrite(int Start, int Size, byte[] Buffer)
		{
			return Cli_ABWrite(Client, Start, Size, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_TMRead(IntPtr Client, int Start, int Amount, ushort[] Buffer);
		public int TMRead(int Start, int Amount, ushort[] Buffer)
		{
			return Cli_TMRead(Client, Start, Amount, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_TMWrite(IntPtr Client, int Start, int Amount, ushort[] Buffer);
		public int TMWrite(int Start, int Amount, ushort[] Buffer)
		{
			return Cli_TMWrite(Client, Start, Amount, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_CTRead(IntPtr Client, int Start, int Amount, ushort[] Buffer);
		public int CTRead(int Start, int Amount, ushort[] Buffer)
		{
			return Cli_CTRead(Client, Start, Amount, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_CTWrite(IntPtr Client, int Start, int Amount, ushort[] Buffer);
		public int CTWrite(int Start, int Amount, ushort[] Buffer)
		{
			return Cli_CTWrite(Client, Start, Amount, Buffer);
		}

		#endregion

		#region [Directory functions]

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_ListBlocks(IntPtr Client, ref S7BlocksList List);
		public int ListBlocks(ref S7BlocksList List)
		{
			return Cli_ListBlocks(Client, ref List);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_GetAgBlockInfo(IntPtr Client, int BlockType, int BlockNum, ref US7BlockInfo Info);
		public int GetAgBlockInfo(int BlockType, int BlockNum, ref S7BlockInfo Info)
		{
			int res = Cli_GetAgBlockInfo(Client, BlockType, BlockNum, ref UBlockInfo);
			// Packed->Managed
			if (res == 0)
			{
				Info.BlkType = UBlockInfo.BlkType;
				Info.BlkNumber = UBlockInfo.BlkNumber;
				Info.BlkLang = UBlockInfo.BlkLang;
				Info.BlkFlags = UBlockInfo.BlkFlags;
				Info.MC7Size = UBlockInfo.MC7Size;
				Info.LoadSize = UBlockInfo.LoadSize;
				Info.LocalData = UBlockInfo.LocalData;
				Info.SBBLength = UBlockInfo.SBBLength;
				Info.CheckSum = UBlockInfo.CheckSum;
				Info.Version = UBlockInfo.Version;
				// Chars info
				Info.CodeDate = new string(UBlockInfo.CodeDate);
				Info.IntfDate = new string(UBlockInfo.IntfDate);
				Info.Author = new string(UBlockInfo.Author);
				Info.Family = new string(UBlockInfo.Family);
				Info.Header = new string(UBlockInfo.Header);
			}
			return res;
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_GetPgBlockInfo(IntPtr Client, ref US7BlockInfo Info, byte[] Buffer, Int32 Size);
		public int GetPgBlockInfo(ref S7BlockInfo Info, byte[] Buffer, int Size)
		{
			int res = Cli_GetPgBlockInfo(Client, ref UBlockInfo, Buffer, Size);
			// Packed->Managed
			if (res == 0)
			{
				Info.BlkType = UBlockInfo.BlkType;
				Info.BlkNumber = UBlockInfo.BlkNumber;
				Info.BlkLang = UBlockInfo.BlkLang;
				Info.BlkFlags = UBlockInfo.BlkFlags;
				Info.MC7Size = UBlockInfo.MC7Size;
				Info.LoadSize = UBlockInfo.LoadSize;
				Info.LocalData = UBlockInfo.LocalData;
				Info.SBBLength = UBlockInfo.SBBLength;
				Info.CheckSum = UBlockInfo.CheckSum;
				Info.Version = UBlockInfo.Version;
				// Chars info
				Info.CodeDate = new string(UBlockInfo.CodeDate);
				Info.IntfDate = new string(UBlockInfo.IntfDate);
				Info.Author = new string(UBlockInfo.Author);
				Info.Family = new string(UBlockInfo.Family);
				Info.Header = new string(UBlockInfo.Header);
			}
			return res;
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_ListBlocksOfType(IntPtr Client, Int32 BlockType, ushort[] List, ref int ItemsCount);
		public int ListBlocksOfType(int BlockType, ushort[] List, ref int ItemsCount)
		{
			return Cli_ListBlocksOfType(Client, BlockType, List, ref ItemsCount);
		}

		#endregion

		#region [Blocks functions]

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_Upload(IntPtr Client, int BlockType, int BlockNum, byte[] UsrData, ref int Size);
		public int Upload(int BlockType, int BlockNum, byte[] UsrData, ref int Size)
		{
			return Cli_Upload(Client, BlockType, BlockNum, UsrData, ref Size);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_FullUpload(IntPtr Client, int BlockType, int BlockNum, byte[] UsrData, ref int Size);
		public int FullUpload(int BlockType, int BlockNum, byte[] UsrData, ref int Size)
		{
			return Cli_FullUpload(Client, BlockType, BlockNum, UsrData, ref Size);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_Download(IntPtr Client, int BlockNum, byte[] UsrData, int Size);
		public int Download(int BlockNum, byte[] UsrData, int Size)
		{
			return Cli_Download(Client, BlockNum, UsrData, Size);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_Delete(IntPtr Client, int BlockType, int BlockNum);
		public int Delete(int BlockType, int BlockNum)
		{
			return Cli_Delete(Client, BlockType, BlockNum);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_DBGet(IntPtr Client, int DBNumber, byte[] UsrData, ref int Size);
		public int DBGet(int DBNumber, byte[] UsrData, ref int Size)
		{
			return Cli_DBGet(Client, DBNumber, UsrData, ref Size);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_DBFill(IntPtr Client, int DBNumber, int FillChar);
		public int DBFill(int DBNumber, int FillChar)
		{
			return Cli_DBFill(Client, DBNumber, FillChar);
		}

		#endregion

		#region [Date/Time functions]

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_GetPlcDateTime(IntPtr Client, ref cpp_tm tm);
		public int GetPlcDateTime(ref DateTime DT)
		{
			int res = Cli_GetPlcDateTime(Client, ref tm);
			if (res == 0)
			{
				// Packed->Managed
				DateTime PlcDT = new DateTime(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
				DT = PlcDT;
			}
			return res;
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_SetPlcDateTime(IntPtr Client, ref cpp_tm tm);
		public int SetPlcDateTime(DateTime DT)
		{

			// Managed->Packed
			tm.tm_year = DT.Year - 1900;
			tm.tm_mon = DT.Month - 1;
			tm.tm_mday = DT.Day;
			tm.tm_hour = DT.Hour;
			tm.tm_min = DT.Minute;
			tm.tm_sec = DT.Second;

			return Cli_SetPlcDateTime(Client, ref tm);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_SetPlcSystemDateTime(IntPtr Client);
		public int SetPlcSystemDateTime()
		{
			return Cli_SetPlcSystemDateTime(Client);
		}

		#endregion

		#region [System Info functions]

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_GetOrderCode(IntPtr Client, ref US7OrderCode Info);
		public int GetOrderCode(ref S7OrderCode Info)
		{
			int res = Cli_GetOrderCode(Client, ref UOrderCode);
			// Packed->Managed
			if (res == 0)
			{
				Info.Code = new string(UOrderCode.Code);
				Info.V1 = UOrderCode.V1;
				Info.V2 = UOrderCode.V2;
				Info.V3 = UOrderCode.V3;
			}
			return res;
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_GetCpuInfo(IntPtr Client, ref US7CpuInfo Info);
		public int GetCpuInfo(ref S7CpuInfo Info)
		{
			int res = Cli_GetCpuInfo(Client, ref UCpuInfo);
			// Packed->Managed
			if (res == 0)
			{
				Info.ModuleTypeName = new string(UCpuInfo.ModuleTypeName);
				Info.SerialNumber = new string(UCpuInfo.SerialNumber);
				Info.ASName = new string(UCpuInfo.ASName);
				Info.Copyright = new string(UCpuInfo.Copyright);
				Info.ModuleName = new string(UCpuInfo.ModuleName);
			}
			return res;
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_GetCpInfo(IntPtr Client, ref S7CpInfo Info);

		public int GetCpInfo(ref S7CpInfo Info)
		{
			return Cli_GetCpInfo(Client, ref Info);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_ReadSZL(IntPtr Client, int ID, int Index, ref S7SZL Data, ref Int32 Size);
		public int ReadSZL(int ID, int Index, ref S7SZL Data, ref Int32 Size)
		{
			return Cli_ReadSZL(Client, ID, Index, ref Data, ref Size);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_ReadSZLList(IntPtr Client, ref S7SZLList List, ref Int32 ItemsCount);
		public int ReadSZLList(ref S7SZLList List, ref Int32 ItemsCount)
		{
			return Cli_ReadSZLList(Client, ref List, ref ItemsCount);
		}

		#endregion

		#region [Control functions]

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_PlcHotStart(IntPtr Client);
		public int PlcHotStart()
		{
			return Cli_PlcHotStart(Client);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_PlcColdStart(IntPtr Client);
		public int PlcColdStart()
		{
			return Cli_PlcColdStart(Client);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_PlcStop(IntPtr Client);
		public int PlcStop()
		{
			return Cli_PlcStop(Client);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_CopyRamToRom(IntPtr Client, UInt32 Timeout);
		public int PlcCopyRamToRom(UInt32 Timeout)
		{
			return Cli_CopyRamToRom(Client, Timeout);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_Compress(IntPtr Client, UInt32 Timeout);
		public int PlcCompress(UInt32 Timeout)
		{
			return Cli_Compress(Client, Timeout);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_GetPlcStatus(IntPtr Client, ref Int32 Status);
		public int PlcGetStatus(ref Int32 Status)
		{
			return Cli_GetPlcStatus(Client, ref Status);
		}

		#endregion

		#region [Security functions]

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_GetProtection(IntPtr Client, ref S7Protection Protection);
		public int GetProtection(ref S7Protection Protection)
		{
			return Cli_GetProtection(Client, ref Protection);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_SetSessionPassword(IntPtr Client, [MarshalAs(UnmanagedType.LPStr)] string Password);
		public int SetSessionPassword(string Password)
		{
			return Cli_SetSessionPassword(Client, Password);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_ClearSessionPassword(IntPtr Client);
		public int ClearSessionPassword()
		{
			return Cli_ClearSessionPassword(Client);
		}

		#endregion

		#region [Low Level]

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_IsoExchangeBuffer(IntPtr Client, byte[] Buffer, ref Int32 Size);
		public int IsoExchangeBuffer(byte[] Buffer, ref Int32 Size)
		{
			return Cli_IsoExchangeBuffer(Client, Buffer, ref Size);
		}

		#endregion

		#region [Async functions]

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_AsReadArea(IntPtr Client, int Area, int DBNumber, int Start, int Amount, int WordLen, byte[] Buffer);
		public int AsReadArea(int Area, int DBNumber, int Start, int Amount, int WordLen, byte[] Buffer)
		{
			return Cli_AsReadArea(Client, Area, DBNumber, Start, Amount, WordLen, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_AsWriteArea(IntPtr Client, int Area, int DBNumber, int Start, int Amount, int WordLen, byte[] Buffer);
		public int AsWriteArea(int Area, int DBNumber, int Start, int Amount, int WordLen, byte[] Buffer)
		{
			return Cli_AsWriteArea(Client, Area, DBNumber, Start, Amount, WordLen, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_AsDBRead(IntPtr Client, int DBNumber, int Start, int Size, byte[] Buffer);
		public int AsDBRead(int DBNumber, int Start, int Size, byte[] Buffer)
		{
			return Cli_AsDBRead(Client, DBNumber, Start, Size, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_AsDBWrite(IntPtr Client, int DBNumber, int Start, int Size, byte[] Buffer);
		public int AsDBWrite(int DBNumber, int Start, int Size, byte[] Buffer)
		{
			return Cli_AsDBWrite(Client, DBNumber, Start, Size, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_AsMBRead(IntPtr Client, int Start, int Size, byte[] Buffer);
		public int AsMBRead(int Start, int Size, byte[] Buffer)
		{
			return Cli_AsMBRead(Client, Start, Size, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_AsMBWrite(IntPtr Client, int Start, int Size, byte[] Buffer);
		public int AsMBWrite(int Start, int Size, byte[] Buffer)
		{
			return Cli_AsMBWrite(Client, Start, Size, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_AsEBRead(IntPtr Client, int Start, int Size, byte[] Buffer);
		public int AsEBRead(int Start, int Size, byte[] Buffer)
		{
			return Cli_AsEBRead(Client, Start, Size, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_AsEBWrite(IntPtr Client, int Start, int Size, byte[] Buffer);
		public int AsEBWrite(int Start, int Size, byte[] Buffer)
		{
			return Cli_AsEBWrite(Client, Start, Size, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_AsABRead(IntPtr Client, int Start, int Size, byte[] Buffer);
		public int AsABRead(int Start, int Size, byte[] Buffer)
		{
			return Cli_AsABRead(Client, Start, Size, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_AsABWrite(IntPtr Client, int Start, int Size, byte[] Buffer);
		public int AsABWrite(int Start, int Size, byte[] Buffer)
		{
			return Cli_AsABWrite(Client, Start, Size, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_AsTMRead(IntPtr Client, int Start, int Amount, ushort[] Buffer);
		public int AsTMRead(int Start, int Amount, ushort[] Buffer)
		{
			return Cli_AsTMRead(Client, Start, Amount, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_AsTMWrite(IntPtr Client, int Start, int Amount, ushort[] Buffer);
		public int AsTMWrite(int Start, int Amount, ushort[] Buffer)
		{
			return Cli_AsTMWrite(Client, Start, Amount, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_AsCTRead(IntPtr Client, int Start, int Amount, ushort[] Buffer);
		public int AsCTRead(int Start, int Amount, ushort[] Buffer)
		{
			return Cli_AsCTRead(Client, Start, Amount, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_AsCTWrite(IntPtr Client, int Start, int Amount, ushort[] Buffer);
		public int AsCTWrite(int Start, int Amount, ushort[] Buffer)
		{
			return Cli_AsCTWrite(Client, Start, Amount, Buffer);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_AsListBlocksOfType(IntPtr Client, Int32 BlockType, ushort[] List);
		public int AsListBlocksOfType(int BlockType, ushort[] List)
		{
			return Cli_AsListBlocksOfType(Client, BlockType, List);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_AsReadSZL(IntPtr Client, int ID, int Index, ref S7SZL Data, ref Int32 Size);
		public int AsReadSZL(int ID, int Index, ref S7SZL Data, ref Int32 Size)
		{
			return Cli_AsReadSZL(Client, ID, Index, ref Data, ref Size);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_AsReadSZLList(IntPtr Client, ref S7SZLList List, ref Int32 ItemsCount);
		public int AsReadSZLList(ref S7SZLList List, ref Int32 ItemsCount)
		{
			return Cli_AsReadSZLList(Client, ref List, ref ItemsCount);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_AsUpload(IntPtr Client, int BlockType, int BlockNum, byte[] UsrData, ref int Size);
		public int AsUpload(int BlockType, int BlockNum, byte[] UsrData, ref int Size)
		{
			return Cli_AsUpload(Client, BlockType, BlockNum, UsrData, ref Size);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_AsFullUpload(IntPtr Client, int BlockType, int BlockNum, byte[] UsrData, ref int Size);
		public int AsFullUpload(int BlockType, int BlockNum, byte[] UsrData, ref int Size)
		{
			return Cli_AsFullUpload(Client, BlockType, BlockNum, UsrData, ref Size);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_AsDownload(IntPtr Client, int BlockNum, byte[] UsrData, int Size);
		public int ASDownload(int BlockNum, byte[] UsrData, int Size)
		{
			return Cli_AsDownload(Client, BlockNum, UsrData, Size);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_AsPlcCopyRamToRom(IntPtr Client, UInt32 Timeout);
		public int AsPlcCopyRamToRom(UInt32 Timeout)
		{
			return Cli_AsPlcCopyRamToRom(Client, Timeout);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_AsPlcCompress(IntPtr Client, UInt32 Timeout);
		public int AsPlcCompress(UInt32 Timeout)
		{
			return Cli_AsPlcCompress(Client, Timeout);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_AsDBGet(IntPtr Client, int DBNumber, byte[] UsrData, ref int Size);
		public int AsDBGet(int DBNumber, byte[] UsrData, ref int Size)
		{
			return Cli_AsDBGet(Client, DBNumber, UsrData, ref Size);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_AsDBFill(IntPtr Client, int DBNumber, int FillChar);
		public int AsDBFill(int DBNumber, int FillChar)
		{
			return Cli_AsDBFill(Client, DBNumber, FillChar);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_CheckAsCompletion(IntPtr Client, ref Int32 opResult);
		public bool CheckAsCompletion(ref int opResult)
		{
			return Cli_CheckAsCompletion(Client, ref opResult) == JobComplete;
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_WaitAsCompletion(IntPtr Client, Int32 Timeout);
		public int WaitAsCompletion(int Timeout)
		{
			return Cli_WaitAsCompletion(Client, Timeout);
		}

		#endregion

		#region [Info Functions]

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_GetExecTime(IntPtr Client, ref UInt32 Time);
		public int ExecTime()
		{
			UInt32 Time = new UInt32();
			if (Cli_GetExecTime(Client, ref Time) == 0)
				return (int)(Time);
			else
				return -1;
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_GetLastError(IntPtr Client, ref Int32 LastError);
		public int LastError()
		{
			Int32 ClientLastError = new Int32();
			if (Cli_GetLastError(Client, ref ClientLastError) == 0)
				return (int)ClientLastError;
			else
				return -1;
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_GetPduLength(IntPtr Client, ref Int32 Requested, ref Int32 Negotiated);

		public int RequestedPduLength()
		{
			Int32 Requested = new Int32();
			Int32 Negotiated = new Int32();
			if (Cli_GetPduLength(Client, ref Requested, ref Negotiated) == 0)
				return Requested;
			else
				return -1;
		}

		public int NegotiatedPduLength()
		{
			Int32 Requested = new Int32();
			Int32 Negotiated = new Int32();
			if (Cli_GetPduLength(Client, ref Requested, ref Negotiated) == 0)
				return Negotiated;
			else
				return -1;
		}

		[DllImport(S7Consts.Snap7LibName, CharSet = CharSet.Ansi)]
		protected static extern int Cli_ErrorText(int Error, StringBuilder ErrMsg, int TextSize);
		public string ErrorText(int Error)
		{
			StringBuilder Message = new StringBuilder(MsgTextLen);
			Cli_ErrorText(Error, Message, MsgTextLen);
			return Message.ToString();
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Cli_GetConnected(IntPtr Client, ref UInt32 IsConnected);
		public bool Connected()
		{
			UInt32 IsConnected = new UInt32();
			if (Cli_GetConnected(Client, ref IsConnected) == 0)
				return IsConnected != 0;
			else
				return false;
		}

		#endregion
	}

	public class S7Server
	{
		#region [Constants, private vars and TypeDefs]

		private const int MsgTextLen = 1024;
		private const int mkEvent = 0;
		private const int mkLog = 1;

		// S7 Area ID
		public const byte S7AreaPE = 0x81;
		public const byte S7AreaPA = 0x82;
		public const byte S7AreaMK = 0x83;
		public const byte S7AreaDB = 0x84;
		public const byte S7AreaCT = 0x1C;
		public const byte S7AreaTM = 0x1D;
		// S7 Word Length
		public const int S7WLBit = 0x01;
		public const int S7WLByte = 0x02;
		public const int S7WLWord = 0x04;
		public const int S7WLDWord = 0x06;
		public const int S7WLReal = 0x08;
		public const int S7WLCounter = 0x1C;
		public const int S7WLTimer = 0x1D;

		// Server Area ID  (use with Register/unregister - Lock/unlock Area)
		public static readonly int srvAreaPE = 0;
		public static readonly int srvAreaPA = 1;
		public static readonly int srvAreaMK = 2;
		public static readonly int srvAreaCT = 3;
		public static readonly int srvAreaTM = 4;
		public static readonly int srvAreaDB = 5;
		// Errors
		public static readonly uint errSrvCannotStart = 0x00100000; // Server cannot start
		public static readonly uint errSrvDBNullPointer = 0x00200000; // Passed null as PData
		public static readonly uint errSrvAreaAlreadyExists = 0x00300000; // Area Re-registration
		public static readonly uint errSrvUnknownArea = 0x00400000; // Unknown area
		public static readonly uint errSrvInvalidParams = 0x00500000; // Invalid param(s) supplied
		public static readonly uint errSrvTooManyDB = 0x00600000; // Cannot register DB
		public static readonly uint errSrvInvalidParamNumber = 0x00700000; // Invalid param (srv_get/set_param)
		public static readonly uint errSrvCannotChangeParam = 0x00800000; // Cannot change because running
																		  // TCP Server Event codes
		public static readonly uint evcServerStarted = 0x00000001;
		public static readonly uint evcServerStopped = 0x00000002;
		public static readonly uint evcListenerCannotStart = 0x00000004;
		public static readonly uint evcClientAdded = 0x00000008;
		public static readonly uint evcClientRejected = 0x00000010;
		public static readonly uint evcClientNoRoom = 0x00000020;
		public static readonly uint evcClientException = 0x00000040;
		public static readonly uint evcClientDisconnected = 0x00000080;
		public static readonly uint evcClientTerminated = 0x00000100;
		public static readonly uint evcClientsDropped = 0x00000200;
		public static readonly uint evcReserved_00000400 = 0x00000400; // actually unused
		public static readonly uint evcReserved_00000800 = 0x00000800; // actually unused
		public static readonly uint evcReserved_00001000 = 0x00001000; // actually unused
		public static readonly uint evcReserved_00002000 = 0x00002000; // actually unused
		public static readonly uint evcReserved_00004000 = 0x00004000; // actually unused
		public static readonly uint evcReserved_00008000 = 0x00008000; // actually unused
																	   // S7 Server Event Code
		public static readonly uint evcPDUincoming = 0x00010000;
		public static readonly uint evcDataRead = 0x00020000;
		public static readonly uint evcDataWrite = 0x00040000;
		public static readonly uint evcNegotiatePDU = 0x00080000;
		public static readonly uint evcReadSZL = 0x00100000;
		public static readonly uint evcClock = 0x00200000;
		public static readonly uint evcUpload = 0x00400000;
		public static readonly uint evcDownload = 0x00800000;
		public static readonly uint evcDirectory = 0x01000000;
		public static readonly uint evcSecurity = 0x02000000;
		public static readonly uint evcControl = 0x04000000;
		public static readonly uint evcReserved_08000000 = 0x08000000; // actually unused
		public static readonly uint evcReserved_10000000 = 0x10000000; // actually unused
		public static readonly uint evcReserved_20000000 = 0x20000000; // actually unused
		public static readonly uint evcReserved_40000000 = 0x40000000; // actually unused
		public static readonly uint evcReserved_80000000 = 0x80000000; // actually unused
																	   // Masks to enable/disable all events
		public static readonly uint evcAll = 0xFFFFFFFF;
		public static readonly uint evcNone = 0x00000000;
		// Event SubCodes
		public static readonly ushort evsUnknown = 0x0000;
		public static readonly ushort evsStartUpload = 0x0001;
		public static readonly ushort evsStartDownload = 0x0001;
		public static readonly ushort evsGetBlockList = 0x0001;
		public static readonly ushort evsStartListBoT = 0x0002;
		public static readonly ushort evsListBoT = 0x0003;
		public static readonly ushort evsGetBlockInfo = 0x0004;
		public static readonly ushort evsGetClock = 0x0001;
		public static readonly ushort evsSetClock = 0x0002;
		public static readonly ushort evsSetPassword = 0x0001;
		public static readonly ushort evsClrPassword = 0x0002;
		// Event Params : functions group
		public static readonly ushort grProgrammer = 0x0041;
		public static readonly ushort grCyclicData = 0x0042;
		public static readonly ushort grBlocksInfo = 0x0043;
		public static readonly ushort grSZL = 0x0044;
		public static readonly ushort grPassword = 0x0045;
		public static readonly ushort grBSend = 0x0046;
		public static readonly ushort grClock = 0x0047;
		public static readonly ushort grSecurity = 0x0045;
		// Event Params : control codes
		public static readonly ushort CodeControlUnknown = 0x0000;
		public static readonly ushort CodeControlColdStart = 0x0001;
		public static readonly ushort CodeControlWarmStart = 0x0002;
		public static readonly ushort CodeControlStop = 0x0003;
		public static readonly ushort CodeControlCompress = 0x0004;
		public static readonly ushort CodeControlCpyRamRom = 0x0005;
		public static readonly ushort CodeControlInsDel = 0x0006;
		// Event Result
		public static readonly ushort evrNoError = 0x0000;
		public static readonly ushort evrFragmentRejected = 0x0001;
		public static readonly ushort evrMalformedPDU = 0x0002;
		public static readonly ushort evrSparseBytes = 0x0003;
		public static readonly ushort evrCannotHandlePDU = 0x0004;
		public static readonly ushort evrNotImplemented = 0x0005;
		public static readonly ushort evrErrException = 0x0006;
		public static readonly ushort evrErrAreaNotFound = 0x0007;
		public static readonly ushort evrErrOutOfRange = 0x0008;
		public static readonly ushort evrErrOverPDU = 0x0009;
		public static readonly ushort evrErrTransportSize = 0x000A;
		public static readonly ushort evrInvalidGroupUData = 0x000B;
		public static readonly ushort evrInvalidSZL = 0x000C;
		public static readonly ushort evrDataSizeMismatch = 0x000D;
		public static readonly ushort evrCannotUpload = 0x000E;
		public static readonly ushort evrCannotDownload = 0x000F;
		public static readonly ushort evrUploadInvalidID = 0x0010;
		public static readonly ushort evrResNotFound = 0x0011;

		// Read/Write Operation (to be used into RWCallback)
		public static readonly int OperationRead = 0;
		public static readonly int OperationWrite = 1;

		[StructLayout(LayoutKind.Sequential, Pack = 1)]
		public struct USrvEvent
		{
			public IntPtr EvtTime;   // It's platform dependent (32 or 64 bit)
			public Int32 EvtSender;
			public UInt32 EvtCode;
			public ushort EvtRetCode;
			public ushort EvtParam1;
			public ushort EvtParam2;
			public ushort EvtParam3;
			public ushort EvtParam4;
		}

		public struct SrvEvent
		{
			public DateTime EvtTime;
			public Int32 EvtSender;
			public UInt32 EvtCode;
			public ushort EvtRetCode;
			public ushort EvtParam1;
			public ushort EvtParam2;
			public ushort EvtParam3;
			public ushort EvtParam4;
		}

		private Dictionary<Int32, GCHandle> HArea;

		private IntPtr Server;

		#endregion

		#region [Class Control]

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern IntPtr Srv_Create();
		public S7Server()
		{
			Server = Srv_Create();
			HArea = new Dictionary<int, GCHandle>();
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Srv_Destroy(ref IntPtr Server);
		~S7Server()
		{
			foreach (var Item in HArea)
			{
				GCHandle handle = Item.Value;
				if (handle != null)
					handle.Free();
			}
			Srv_Destroy(ref Server);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Srv_StartTo(IntPtr Server, [MarshalAs(UnmanagedType.LPStr)] string Address);
		public int StartTo(string Address)
		{
			return Srv_StartTo(Server, Address);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Srv_Start(IntPtr Server);
		public int Start()
		{
			return Srv_Start(Server);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Srv_Stop(IntPtr Server);
		public int Stop()
		{
			return Srv_Stop(Server);
		}

		// Get/SetParam needs a void* parameter, internally it decides the kind of pointer
		// in accord to ParamNumber.
		// To avoid the use of unsafe code we split the DLL functions and use overloaded methods.

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Srv_GetParam")]
		protected static extern int Srv_GetParam_i16(IntPtr Server, Int32 ParamNumber, ref Int16 IntValue);
		public int GetParam(Int32 ParamNumber, ref Int16 IntValue)
		{
			return Srv_GetParam_i16(Server, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Srv_GetParam")]
		protected static extern int Srv_GetParam_u16(IntPtr Server, Int32 ParamNumber, ref UInt16 IntValue);
		public int GetParam(Int32 ParamNumber, ref UInt16 IntValue)
		{
			return Srv_GetParam_u16(Server, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Srv_GetParam")]
		protected static extern int Srv_GetParam_i32(IntPtr Server, Int32 ParamNumber, ref Int32 IntValue);
		public int GetParam(Int32 ParamNumber, ref Int32 IntValue)
		{
			return Srv_GetParam_i32(Server, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Srv_GetParam")]
		protected static extern int Srv_GetParam_u32(IntPtr Server, Int32 ParamNumber, ref UInt32 IntValue);
		public int GetParam(Int32 ParamNumber, ref UInt32 IntValue)
		{
			return Srv_GetParam_u32(Server, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Srv_GetParam")]
		protected static extern int Srv_GetParam_i64(IntPtr Server, Int32 ParamNumber, ref Int64 IntValue);
		public int GetParam(Int32 ParamNumber, ref Int64 IntValue)
		{
			return Srv_GetParam_i64(Server, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Srv_GetParam")]
		protected static extern int Srv_GetParam_u64(IntPtr Server, Int32 ParamNumber, ref UInt64 IntValue);
		public int GetParam(Int32 ParamNumber, ref UInt64 IntValue)
		{
			return Srv_GetParam_u64(Server, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Srv_SetParam")]
		protected static extern int Srv_SetParam_i16(IntPtr Server, Int32 ParamNumber, ref Int16 IntValue);
		public int SetParam(Int32 ParamNumber, ref Int16 IntValue)
		{
			return Srv_SetParam_i16(Server, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Srv_SetParam")]
		protected static extern int Srv_SetParam_u16(IntPtr Server, Int32 ParamNumber, ref UInt16 IntValue);
		public int SetParam(Int32 ParamNumber, ref UInt16 IntValue)
		{
			return Srv_SetParam_u16(Server, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Srv_SetParam")]
		protected static extern int Srv_SetParam_i32(IntPtr Server, Int32 ParamNumber, ref Int32 IntValue);
		public int SetParam(Int32 ParamNumber, ref Int32 IntValue)
		{
			return Srv_SetParam_i32(Server, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Srv_SetParam")]
		protected static extern int Srv_SetParam_u32(IntPtr Server, Int32 ParamNumber, ref UInt32 IntValue);
		public int SetParam(Int32 ParamNumber, ref UInt32 IntValue)
		{
			return Srv_SetParam_u32(Server, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Srv_SetParam")]
		protected static extern int Srv_SetParam_i64(IntPtr Server, Int32 ParamNumber, ref Int64 IntValue);
		public int SetParam(Int32 ParamNumber, ref Int64 IntValue)
		{
			return Srv_SetParam_i64(Server, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Srv_SetParam")]
		protected static extern int Srv_SetParam_u64(IntPtr Server, Int32 ParamNumber, ref UInt64 IntValue);
		public int SetParam(Int32 ParamNumber, ref UInt64 IntValue)
		{
			return Srv_SetParam_u64(Server, ParamNumber, ref IntValue);
		}

		#endregion

		#region [Data Areas functions]

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Srv_RegisterArea(IntPtr Server, Int32 AreaCode, Int32 Index, IntPtr pUsrData, Int32 Size);
		public int RegisterArea<T>(Int32 AreaCode, Int32 Index, ref T pUsrData, Int32 Size)
		{
			Int32 AreaUID = (AreaCode << 16) + Index;
			GCHandle handle = GCHandle.Alloc(pUsrData, GCHandleType.Pinned);
			int Result = Srv_RegisterArea(Server, AreaCode, Index, handle.AddrOfPinnedObject(), Size);
			if (Result == 0)
				HArea.Add(AreaUID, handle);
			else
				handle.Free();
			return Result;
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Srv_UnregisterArea(IntPtr Server, Int32 AreaCode, Int32 Index);
		public int UnregisterArea(Int32 AreaCode, Int32 Index)
		{
			int Result = Srv_UnregisterArea(Server, AreaCode, Index);
			if (Result == 0)
			{
				Int32 AreaUID = (AreaCode << 16) + Index;
				if (HArea.ContainsKey(AreaUID)) // should be always true
				{
					GCHandle handle = HArea[AreaUID];
					if (handle != null) // should be always true
						handle.Free();
					HArea.Remove(AreaUID);
				}
			}
			return Result;
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Srv_LockArea(IntPtr Server, Int32 AreaCode, Int32 Index);
		public int LockArea(Int32 AreaCode, Int32 Index)
		{
			return Srv_LockArea(Server, AreaCode, Index);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Srv_UnlockArea(IntPtr Server, Int32 AreaCode, Int32 Index);
		public int UnlockArea(Int32 AreaCode, Int32 Index)
		{
			return Srv_UnlockArea(Server, AreaCode, Index);
		}

		#endregion

		#region [Notification functions (Events)]

		[StructLayout(LayoutKind.Sequential, Pack = 1)]
		public struct RWBuffer
		{
			[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024)] // A telegram cannot exceed PDU size (960 bytes)
			public byte[] Data;
		}

		public delegate void TSrvCallback(IntPtr usrPtr, ref USrvEvent Event, int Size);
		public delegate int TSrvRWAreaCallback(IntPtr usrPtr, int Sender, int Operation, ref S7Consts.S7Tag Tag, ref RWBuffer Buffer);

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Srv_SetEventsCallback(IntPtr Server, TSrvCallback Callback, IntPtr usrPtr);
		public int SetEventsCallBack(TSrvCallback Callback, IntPtr usrPtr)
		{
			return Srv_SetEventsCallback(Server, Callback, usrPtr);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Srv_SetReadEventsCallback(IntPtr Server, TSrvCallback Callback, IntPtr usrPtr);
		public int SetReadEventsCallBack(TSrvCallback Callback, IntPtr usrPtr)
		{
			return Srv_SetReadEventsCallback(Server, Callback, usrPtr);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Srv_SetRWAreaCallback(IntPtr Server, TSrvRWAreaCallback Callback, IntPtr usrPtr);
		public int SetRWAreaCallBack(TSrvRWAreaCallback Callback, IntPtr usrPtr)
		{
			return Srv_SetRWAreaCallback(Server, Callback, usrPtr);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Srv_PickEvent(IntPtr Server, ref USrvEvent Event, ref Int32 EvtReady);
		public bool PickEvent(ref USrvEvent Event)
		{
			Int32 EvtReady = new Int32();
			if (Srv_PickEvent(Server, ref Event, ref EvtReady) == 0)
				return EvtReady != 0;
			else
				return false;
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Srv_ClearEvents(IntPtr Server);
		public int ClearEvents()
		{
			return Srv_ClearEvents(Server);
		}

		[DllImport(S7Consts.Snap7LibName, CharSet = CharSet.Ansi)]
		protected static extern int Srv_EventText(ref USrvEvent Event, StringBuilder EvtMsg, int TextSize);
		public string EventText(ref USrvEvent Event)
		{
			StringBuilder Message = new StringBuilder(MsgTextLen);
			Srv_EventText(ref Event, Message, MsgTextLen);
			return Message.ToString();
		}

		public DateTime EvtTimeToDateTime(IntPtr TimeStamp)
		{
			DateTime UnixStartEpoch = new DateTime(1970, 1, 1, 0, 0, 0, 0);
			return UnixStartEpoch.AddSeconds(Convert.ToDouble(TimeStamp));
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Srv_GetMask(IntPtr Server, Int32 MaskKind, ref UInt32 Mask);
		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Srv_SetMask(IntPtr Server, Int32 MaskKind, UInt32 Mask);

		// Property LogMask R/W
		public UInt32 LogMask
		{
			get
			{
				UInt32 Mask = new UInt32();
				if (Srv_GetMask(Server, S7Server.mkLog, ref Mask) == 0)
					return Mask;
				else
					return 0;
			}
			set
			{
				Srv_SetMask(Server, S7Server.mkLog, value);
			}
		}

		// Property EventMask R/W
		public UInt32 EventMask
		{
			get
			{
				UInt32 Mask = new UInt32();
				if (Srv_GetMask(Server, S7Server.mkEvent, ref Mask) == 0)
					return Mask;
				else
					return 0;
			}
			set
			{
				Srv_SetMask(Server, S7Server.mkEvent, value);
			}
		}


		#endregion

		#region [Info functions]

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Srv_GetStatus(IntPtr Server, ref Int32 ServerStatus, ref Int32 CpuStatus, ref Int32 ClientsCount);
		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Srv_SetCpuStatus(IntPtr Server, Int32 CpuStatus);

		// Property Virtual CPU status R/W
		public int CpuStatus
		{
			get
			{
				Int32 CStatus = new Int32();
				Int32 SStatus = new Int32();
				Int32 CCount = new Int32();

				if (Srv_GetStatus(Server, ref SStatus, ref CStatus, ref CCount) == 0)
					return CStatus;
				else
					return -1;
			}
			set
			{
				Srv_SetCpuStatus(Server, value);
			}
		}

		// Property Server Status Read Only
		public int ServerStatus
		{
			get
			{
				Int32 CStatus = new Int32();
				Int32 SStatus = new Int32();
				Int32 CCount = new Int32();
				if (Srv_GetStatus(Server, ref SStatus, ref CStatus, ref CCount) == 0)
					return SStatus;
				else
					return -1;
			}
		}

		// Property Clients Count Read Only
		public int ClientsCount
		{
			get
			{
				Int32 CStatus = new Int32();
				Int32 SStatus = new Int32();
				Int32 CCount = new Int32();
				if (Srv_GetStatus(Server, ref CStatus, ref SStatus, ref CCount) == 0)
					return CCount;
				else
					return -1;
			}
		}

		[DllImport(S7Consts.Snap7LibName, CharSet = CharSet.Ansi)]
		protected static extern int Srv_ErrorText(int Error, StringBuilder ErrMsg, int TextSize);
		public string ErrorText(int Error)
		{
			StringBuilder Message = new StringBuilder(MsgTextLen);
			Srv_ErrorText(Error, Message, MsgTextLen);
			return Message.ToString();
		}

		#endregion
	}

	public class S7Partner
	{
		#region [Constants, private vars and TypeDefs]

		private const int MsgTextLen = 1024;

		// Status
		public static readonly int par_stopped = 0;   // stopped
		public static readonly int par_connecting = 1;   // running and active connecting
		public static readonly int par_waiting = 2;   // running and waiting for a connection
		public static readonly int par_linked = 3;   // running and connected : linked
		public static readonly int par_sending = 4;   // sending data
		public static readonly int par_receiving = 5;   // receiving data
		public static readonly int par_binderror = 6;   // error starting passive server

		// Errors
		public static readonly uint errParAddressInUse = 0x00200000;
		public static readonly uint errParNoRoom = 0x00300000;
		public static readonly uint errServerNoRoom = 0x00400000;
		public static readonly uint errParInvalidParams = 0x00500000;
		public static readonly uint errParNotLinked = 0x00600000;
		public static readonly uint errParBusy = 0x00700000;
		public static readonly uint errParFrameTimeout = 0x00800000;
		public static readonly uint errParInvalidPDU = 0x00900000;
		public static readonly uint errParSendTimeout = 0x00A00000;
		public static readonly uint errParRecvTimeout = 0x00B00000;
		public static readonly uint errParSendRefused = 0x00C00000;
		public static readonly uint errParNegotiatingPDU = 0x00D00000;
		public static readonly uint errParSendingBlock = 0x00E00000;
		public static readonly uint errParRecvingBlock = 0x00F00000;
		public static readonly uint errBindError = 0x01000000;
		public static readonly uint errParDestroying = 0x01100000;
		public static readonly uint errParInvalidParamNumber = 0x01200000;
		public static readonly uint errParCannotChangeParam = 0x01300000;

		// Generic byte buffer structure, you may need to declare a more
		// specialistic one in your program.
		// It's used to cast the input pointer that cames from the callback.
		// See the passive partned demo and the delegate S7ParRecvCallback.

		[StructLayout(LayoutKind.Sequential, Pack = 1)]
		public struct S7Buffer
		{
			[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x8000)]
			public byte[] Data;
		}

		// Job status
		private const int JobComplete = 0;
		private const int JobPending = 1;

		private IntPtr Partner;

		private Int32 parBytesSent;
		private Int32 parBytesRecv;
		private Int32 parSendErrors;
		private Int32 parRecvErrors;

		#endregion

		#region [Class Control]

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern IntPtr Par_Create(Int32 ParActive);
		public S7Partner(int Active)
		{
			Partner = Par_Create(Active);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Par_Destroy(ref IntPtr Partner);
		~S7Partner()
		{
			Par_Destroy(ref Partner);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Par_StartTo(
			IntPtr Partner,
			[MarshalAs(UnmanagedType.LPStr)] string LocalAddress,
			[MarshalAs(UnmanagedType.LPStr)] string RemoteAddress,
			UInt16 LocalTSAP,
			UInt16 RemoteTSAP);

		public int StartTo(
			string LocalAddress,
			string RemoteAddress,
			UInt16 LocalTSAP,
			UInt16 RemoteTSAP)
		{
			return Par_StartTo(Partner, LocalAddress, RemoteAddress, LocalTSAP, RemoteTSAP);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Par_Start(IntPtr Partner);
		public int Start()
		{
			return Par_Start(Partner);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Par_Stop(IntPtr Partner);
		public int Stop()
		{
			return Par_Stop(Partner);
		}

		// Get/SetParam needs a void* parameter, internally it decides the kind of pointer
		// in accord to ParamNumber.
		// To avoid the use of unsafe code we split the DLL functions and use overloaded methods.

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Par_GetParam")]
		protected static extern int Par_GetParam_i16(IntPtr Partner, Int32 ParamNumber, ref Int16 IntValue);
		public int GetParam(Int32 ParamNumber, ref Int16 IntValue)
		{
			return Par_GetParam_i16(Partner, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Par_GetParam")]
		protected static extern int Par_GetParam_u16(IntPtr Partner, Int32 ParamNumber, ref UInt16 IntValue);
		public int GetParam(Int32 ParamNumber, ref UInt16 IntValue)
		{
			return Par_GetParam_u16(Partner, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Par_GetParam")]
		protected static extern int Par_GetParam_i32(IntPtr Partner, Int32 ParamNumber, ref Int32 IntValue);
		public int GetParam(Int32 ParamNumber, ref Int32 IntValue)
		{
			return Par_GetParam_i32(Partner, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Par_GetParam")]
		protected static extern int Par_GetParam_u32(IntPtr Partner, Int32 ParamNumber, ref UInt32 IntValue);
		public int GetParam(Int32 ParamNumber, ref UInt32 IntValue)
		{
			return Par_GetParam_u32(Partner, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Par_GetParam")]
		protected static extern int Par_GetParam_i64(IntPtr Partner, Int32 ParamNumber, ref Int64 IntValue);
		public int GetParam(Int32 ParamNumber, ref Int64 IntValue)
		{
			return Par_GetParam_i64(Partner, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Par_GetParam")]
		protected static extern int Par_GetParam_u64(IntPtr Partner, Int32 ParamNumber, ref UInt64 IntValue);
		public int GetParam(Int32 ParamNumber, ref UInt64 IntValue)
		{
			return Par_GetParam_u64(Partner, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Par_SetParam")]
		protected static extern int Par_SetParam_i16(IntPtr Partner, Int32 ParamNumber, ref Int16 IntValue);
		public int SetParam(Int32 ParamNumber, ref Int16 IntValue)
		{
			return Par_SetParam_i16(Partner, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Par_SetParam")]
		protected static extern int Par_SetParam_u16(IntPtr Partner, Int32 ParamNumber, ref UInt16 IntValue);
		public int SetParam(Int32 ParamNumber, ref UInt16 IntValue)
		{
			return Par_SetParam_u16(Partner, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Par_SetParam")]
		protected static extern int Par_SetParam_i32(IntPtr Partner, Int32 ParamNumber, ref Int32 IntValue);
		public int SetParam(Int32 ParamNumber, ref Int32 IntValue)
		{
			return Par_SetParam_i32(Partner, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Par_SetParam")]
		protected static extern int Par_SetParam_u32(IntPtr Partner, Int32 ParamNumber, ref UInt32 IntValue);
		public int SetParam(Int32 ParamNumber, ref UInt32 IntValue)
		{
			return Par_SetParam_u32(Partner, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Par_SetParam")]
		protected static extern int Par_SetParam_i64(IntPtr Partner, Int32 ParamNumber, ref Int64 IntValue);
		public int SetParam(Int32 ParamNumber, ref Int64 IntValue)
		{
			return Par_SetParam_i64(Partner, ParamNumber, ref IntValue);
		}

		[DllImport(S7Consts.Snap7LibName, EntryPoint = "Par_SetParam")]
		protected static extern int Par_SetParam_u64(IntPtr Partner, Int32 ParamNumber, ref UInt64 IntValue);
		public int SetParam(Int32 ParamNumber, ref UInt64 IntValue)
		{
			return Par_SetParam_u64(Partner, ParamNumber, ref IntValue);
		}

		#endregion

		#region [Data I/O functions : BSend]

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Par_BSend(IntPtr Partner, UInt32 R_ID, byte[] Buffer, Int32 Size);
		public int BSend(UInt32 R_ID, byte[] Buffer, Int32 Size)
		{
			return Par_BSend(Partner, R_ID, Buffer, Size);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Par_AsBSend(IntPtr Partner, UInt32 R_ID, byte[] Buffer, Int32 Size);
		public int AsBSend(UInt32 R_ID, byte[] Buffer, Int32 Size)
		{
			return Par_AsBSend(Partner, R_ID, Buffer, Size);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Par_CheckAsBSendCompletion(IntPtr Partner, ref Int32 opResult);
		public bool CheckAsBSendCompletion(ref int opResult)
		{
			return Par_CheckAsBSendCompletion(Partner, ref opResult) == JobComplete;
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Par_WaitAsBSendCompletion(IntPtr Partner, Int32 Timeout);
		public int WaitAsBSendCompletion(int Timeout)
		{
			return Par_WaitAsBSendCompletion(Partner, Timeout);
		}

		public delegate void S7ParSendCompletion(IntPtr usrPtr, int opResult);

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Par_SetSendCallback(IntPtr Partner, S7ParSendCompletion Completion, IntPtr usrPtr);
		public int SetSendCallBack(S7ParSendCompletion Completion, IntPtr usrPtr)
		{
			return Par_SetSendCallback(Partner, Completion, usrPtr);
		}

		#endregion

		#region [Data I/O functions : BRecv]

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Par_BRecv(IntPtr Partner, ref UInt32 R_ID, byte[] Buffer, ref Int32 Size, UInt32 Timeout);
		public int BRecv(ref UInt32 R_ID, byte[] Buffer, ref Int32 Size, UInt32 Timeout)
		{
			return Par_BRecv(Partner, ref R_ID, Buffer, ref Size, Timeout);
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Par_CheckAsBRecvCompletion(IntPtr Partner, ref Int32 opResult, ref UInt32 R_ID, byte[] Buffer, ref Int32 Size);
		public bool CheckAsBRecvCompletion(ref Int32 opResult, ref UInt32 R_ID, byte[] Buffer, ref Int32 Size)
		{
			Par_CheckAsBRecvCompletion(Partner, ref opResult, ref R_ID, Buffer, ref Size);
			return opResult == JobComplete;
		}

		public delegate void S7ParRecvCallback(IntPtr usrPtr, int opResult, uint R_ID, IntPtr pData, int Size);

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Par_SetRecvCallback(IntPtr Partner, S7ParRecvCallback Callback, IntPtr usrPtr);
		public int SetRecvCallback(S7ParRecvCallback Callback, IntPtr usrPtr)
		{
			return Par_SetRecvCallback(Partner, Callback, usrPtr);
		}

		#endregion

		#region [Info functions]

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Par_GetLastError(IntPtr Partner, ref Int32 LastError);
		public int LastError(ref Int32 LastError)
		{
			Int32 PartnerLastError = new Int32();
			if (Par_GetLastError(Partner, ref PartnerLastError) == 0)
				return (int)PartnerLastError;
			else
				return -1;
		}

		[DllImport(S7Consts.Snap7LibName, CharSet = CharSet.Ansi)]
		protected static extern int Par_ErrorText(int Error, StringBuilder ErrMsg, int TextSize);
		public string ErrorText(int Error)
		{
			StringBuilder Message = new StringBuilder(MsgTextLen);
			Par_ErrorText(Error, Message, MsgTextLen);
			return Message.ToString();
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Par_GetStats(IntPtr Partner, ref Int32 BytesSent, ref Int32 BytesRecv,
		   ref Int32 SendErrors, ref Int32 RecvErrors);

		private void GetStatistics()
		{
			if (Par_GetStats(Partner, ref parBytesSent, ref parBytesRecv, ref parSendErrors, ref parRecvErrors) != 0)
			{
				parBytesSent = -1;
				parBytesRecv = -1;
				parSendErrors = -1;
				parRecvErrors = -1;
			}
		}

		public int BytesSent
		{
			get
			{
				GetStatistics();
				return parBytesSent;
			}
		}

		public int BytesRecv
		{
			get
			{
				GetStatistics();
				return parBytesRecv;
			}
		}

		public int SendErrors
		{
			get
			{
				GetStatistics();
				return parSendErrors;
			}
		}

		public int RecvErrors
		{
			get
			{
				GetStatistics();
				return parRecvErrors;
			}
		}

		[DllImport(S7Consts.Snap7LibName)]
		protected static extern int Par_GetStatus(IntPtr Partner, ref Int32 Status);

		public int Status
		{
			get
			{
				int ParStatus = new int();
				if (Par_GetStatus(Partner, ref ParStatus) != 0)
					return -1;
				else
					return ParStatus;
			}
		}
		// simply useful
		public bool Linked
		{
			get
			{
				return Status == par_linked;
			}
		}
		#endregion

	}
}
